package configs

import (
	"fmt"
	"github.com/fsnotify/fsnotify"
	"github.com/gostagging/consts"
	"github.com/gostagging/enums"
	"github.com/joho/godotenv"
	"github.com/spf13/viper"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// 定义全局配置结构体
type Config struct {
	Env        string `json:"env"`
	ConfigType string `json:"configType"` // 配置中心类型，nacos  apollo
	FileFormat string `json:"fileFormat"` // 配置文件格式，yaml  properties ini json
	Apollo     Apollo `json:"apollo"`
	Nacos      Nacos  `json:"nacos"`
	Mysql      Mysql  `json:"mysql"`
	Redis      Redis  `json:"redis"`
	Server     Server `json:"server"`
	Jwt        Jwt    `json:"jwt"`
	Mongo      Mongo  `json:"mongo"`
}

var (
	configLock     = new(sync.RWMutex)
	lastReload     time.Time
	reloadThrottle = 500 * time.Millisecond
	EnvConfig      *Config
)

func getExtensions() []string {
	extensions := []string{"yaml", "yml", "json", "toml", "ini", "properties"}
	return extensions
}

// 项目根目录，解决单元测试时，找不到.env问题
func getRootDir() (string, error) {
	// 获取当前文件的路径
	//_, dir, _, _ := runtime.Caller(0)
	dir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Error getting current working directory:")
		return "", err
	}

	splitPath := string(filepath.Separator) + "internal"
	// 假设路径为 "/Workspace/golang/go-stagging/internal/prob/tools/common/"
	parts := strings.Split(dir, splitPath)
	dir = strings.Join(parts[:1], splitPath)

	// 构造绝对路径
	rootDir, err := filepath.Abs(dir)
	if err != nil {
		log.Fatal("Error getting absolute path:", err)
		return "", err
	}
	return rootDir, nil
}

// 获取环境变量值
func getEnvName() string {
	rootDir, err := getRootDir()
	if err != nil {
		log.Fatalf("Unable to get the project root directory")
		panic(err)
	}
	// 获取 .env 文件所在目录
	filePath := filepath.Join(rootDir, consts.ENV_FILE_NAME)

	// 加载 .env 文件（如果需要）
	err = godotenv.Load(filePath)
	if err != nil {
		log.Fatalf("Error loading .env file %v", err)
		return ""
	}

	// 获取当前环境（从环境变量读取）
	env := os.Getenv(consts.ENV_NAME) // get curent path

	log.Println("Getting DEV_ENV Is :", env)
	if env == "" {
		log.Fatal("DEV_ENV is not set")
		return ""
	}

	return env
}

// 自动检测配置文件类型
func getConfigType() string {
	for _, ext := range getExtensions() {
		if _, err := os.Stat(fmt.Sprintf("%s.%s", getConfigName(""), ext)); err == nil {
			return ext
		}
	}
	return "yaml" // 默认类型
}

// 获取环境相关的配置文件名
func getConfigName(prefix string) string {
	env := getEnvName()
	if prefix != "" {
		return fmt.Sprintf("%s-%s", prefix, env)
	}
	return env
}

// 获取当前本地yaml配置文件路径
func getLocalPath(format string) (string, error) {
	rootDir, err := getRootDir()
	if err != nil {
		log.Fatalf("Unable to get the project root directory")
		panic(err)
	}

	rootDir = rootDir + string(filepath.Separator) + "configs" + string(filepath.Separator)
	// 根据环境变量选择不同的配置文件
	var configFileName string
	env := getEnvName()

	switch strings.ToLower(env) {
	case enums.DEV.Name:
		configFileName = rootDir + enums.DEV.Name + "." + format
	case enums.PROD.Name:
		configFileName = rootDir + enums.DEV.Name + "." + format
	case enums.PRE.Name:
		configFileName = rootDir + enums.DEV.Name + "." + format
	case enums.STAGING.Name:
		configFileName = rootDir + enums.DEV.Name + "." + format
	default:
		log.Fatalf("Unknown DEV_ENV value: %s", env)
	}

	return configFileName, nil
}

// 加载配置Struct 并动态更新时刷新使用
func loadConfig() (*Config, error) {
	configLock.Lock()
	defer configLock.Unlock()
	config := &Config{}
	// 读取配置文件并将其映射到结构体
	if err := viper.Unmarshal(config); err != nil {
		log.Fatalf("Error unmarshalling config to struct: %v", err)
		panic(err)
	}

	// 打印映射的配置内容
	log.Printf("Config Mapping Structure %v \n", config)
	EnvConfig = config
	return config, nil
}

// 监听配置变化
func watchConfig() {
	// 设置 Viper 动态监听配置文件变化
	viper.WatchConfig()
	// 设置回调函数，当配置变化时触发
	viper.OnConfigChange(func(e fsnotify.Event) {
		// 配置文件变化时，重新加载配置并更新结构体数据
		// log.Println("Config file changed:", e.Name, viper.GetString("app.name"))
		// 重新加载并更新结构体数据
		config, err := loadConfig()
		if err != nil {
			log.Printf("Config Update Error: %v", err)
			return
		}
		// 输出更新后的配置
		log.Println("Updated Config:", config)
	})
}

// 监听配置变化并更新结构体
func viperListen() {
	// 监听配置变化并更新结构体
	for {
		// 保持程序运行 等待 Viper 配置文件变化并加载新的配置
		select {
		case <-time.After(2 * time.Second): // 轮询检测配置变化
			if viper.ConfigFileUsed() != "" {
				// 读取 Viper 配置更新
				viper.ReadInConfig()

				updatedConfig, err := loadConfig()
				if err != nil {
					log.Printf("Config Update Error: %v", err)
					return
				}

				// 打印更新后的结构体
				log.Printf("Updated Config: %+v\n", updatedConfig)
			}
		}
	}
}

// 尝试加载不同格式的配置文件
func tryLoadConfig() error {
	for _, ext := range getExtensions() {
		configPath, err := getLocalPath(getConfigType())
		if err != nil {
			panic("error occurred " + ext + " path")
		}
		viper.SetConfigType(ext)
		viper.SetConfigFile(configPath)
		// 自动读取文件
		if err := viper.ReadInConfig(); err != nil {
			log.Fatalf("Error reading config file, %s", err)
			panic("Read Config File Err")
		}

		// 将配置文件映射到结构体
		_, err = loadConfig()
		if err != nil {
			log.Fatalf("Config Mapping Structure Error: %v", err)
			panic("Config Mapping Structure Error")
		}
	}
	return fmt.Errorf("no valid config file found")
}

func setupViper() {
	// 尝试自动检测格式
	if err := tryLoadConfig(); err != nil {
		log.Fatalf("Failed to load config: %v", err)
	}
	// 监听配置变化
	watchConfig()

	// 定时监听本地文件变化
	viperListen()
}

// 获取配置副本（线程安全）
func GetConfig() *Config {
	configLock.RLock()
	defer configLock.RUnlock()

	if EnvConfig == nil {
		return &Config{}
	}
	return EnvConfig
}

// 绑定环境变量
func bindEnvVariables() {
	// MySQL
	viper.BindEnv("mysql.host", "APP_MYSQL_HOST")
	viper.BindEnv("mysql.port", "APP_MYSQL_PORT")
	// Redis
	viper.BindEnv("redis.addr", "APP_REDIS_ADDR")
	// MongoDB
	viper.BindEnv("mongodb.uri", "APP_MONGODB_URI")
	// 公共配置
	viper.BindEnv("env", "APP_ENV")
}

func Init() {
	setupViper()

	//判断是否使用配置中心及使用那种配置中心
	if strings.ToLower(EnvConfig.ConfigType) == "nacos" {
		InitNacos()
	} else if strings.ToLower(EnvConfig.ConfigType) == "apollo" {
		InitApollo()
	}

}
